2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../jucer_Headers.h"
27 #include "jucer_PaintRoutine.h"
28 #include "jucer_JucerDocument.h"
29 #include "jucer_ObjectTypes.h"
30 #include "paintelements/jucer_PaintElementUndoableAction.h"
31 #include "paintelements/jucer_PaintElementPath.h"
32 #include "paintelements/jucer_PaintElementImage.h"
33 #include "paintelements/jucer_PaintElementGroup.h"
34 #include "../ui/jucer_JucerDocumentHolder.h"
37 //==============================================================================
38 PaintRoutine::PaintRoutine()
40 backgroundColour (Colours::white
)
45 PaintRoutine::~PaintRoutine()
47 elements
.clear(); // do this explicitly before the scalar destructor because these
48 // objects will be listeners on this object
51 //==============================================================================
52 void PaintRoutine::changed()
58 bool PaintRoutine::perform (UndoableAction
* action
, const String
& actionName
)
60 jassert (document
!= 0);
64 return document
->getUndoManager().perform (action
, actionName
);
74 void PaintRoutine::setBackgroundColour (const Colour
& newColour
) throw()
76 backgroundColour
= newColour
;
80 void PaintRoutine::clear()
82 if (elements
.size() > 0)
89 //==============================================================================
90 class AddXmlElementAction
: public UndoableAction
93 AddXmlElementAction (PaintRoutine
& routine_
, XmlElement
* xml_
)
99 ~AddXmlElementAction()
107 PaintElement
* newElement
= routine
.addElementFromXml (*xml
, -1, false);
108 jassert (newElement
!= 0);
110 indexAdded
= routine
.indexOfElement (newElement
);
111 jassert (indexAdded
>= 0);
112 return indexAdded
>= 0;
118 routine
.removeElement (routine
.getElement (indexAdded
), false);
122 int getSizeInUnits() { return 10; }
127 PaintRoutine
& routine
;
130 void showCorrectTab() const
132 JucerDocumentHolder
* const docHolder
= JucerDocumentHolder::getActiveDocumentHolder();
135 docHolder
->showGraphics (&routine
);
138 AddXmlElementAction (const AddXmlElementAction
&);
139 AddXmlElementAction
& operator= (const AddXmlElementAction
&);
142 PaintElement
* PaintRoutine::addElementFromXml (const XmlElement
& xml
, const int index
, const bool undoable
)
144 selectedPoints
.deselectAll();
148 AddXmlElementAction
* action
= new AddXmlElementAction (*this, new XmlElement (xml
));
149 perform (action
, "Add new element");
151 return elements
[action
->indexAdded
];
155 PaintElement
* const newElement
= ObjectTypes::createElementForXml (&xml
, this);
159 elements
.insert (index
, newElement
);
169 PaintElement
* PaintRoutine::addNewElement (PaintElement
* e
, const int index
, const bool undoable
)
173 XmlElement
* const xml
= e
->createXml();
176 e
= addElementFromXml (*xml
, index
, undoable
);
184 //==============================================================================
185 class DeleteElementAction
: public PaintElementUndoableAction
<PaintElement
>
188 DeleteElementAction (PaintElement
* const element
)
189 : PaintElementUndoableAction
<PaintElement
> (element
),
192 xml
= element
->createXml();
193 oldIndex
= routine
.indexOfElement (element
);
196 ~DeleteElementAction()
204 routine
.removeElement (getElement(), false);
210 PaintElement
* newElement
= routine
.addElementFromXml (*xml
, oldIndex
, false);
212 return newElement
!= 0;
215 int getSizeInUnits() { return 10; }
223 void PaintRoutine::removeElement (PaintElement
* element
, const bool undoable
)
225 if (elements
.contains (element
))
229 perform (new DeleteElementAction (element
),
230 "Delete " + element
->getTypeName());
234 selectedElements
.deselect (element
);
235 selectedPoints
.deselectAll();
237 selectedPoints
.changed (true);
238 selectedElements
.changed (true);
240 elements
.removeObject (element
);
246 //==============================================================================
247 class FrontOrBackElementAction
: public PaintElementUndoableAction
<PaintElement
>
250 FrontOrBackElementAction (PaintElement
* const element
, int newIndex_
)
251 : PaintElementUndoableAction
<PaintElement
> (element
),
254 oldIndex
= routine
.indexOfElement (element
);
261 PaintElement
* e
= routine
.getElement (oldIndex
);
262 routine
.moveElementZOrder (oldIndex
, newIndex
);
263 newIndex
= routine
.indexOfElement (e
);
270 routine
.moveElementZOrder (newIndex
, oldIndex
);
275 int newIndex
, oldIndex
;
278 void PaintRoutine::moveElementZOrder (int oldIndex
, int newIndex
)
280 jassert (elements
[oldIndex
] != 0);
282 if (oldIndex
!= newIndex
&& elements
[oldIndex
] != 0)
284 elements
.move (oldIndex
, newIndex
);
289 void PaintRoutine::elementToFront (PaintElement
* element
, const bool undoable
)
291 if (element
!= 0 && elements
.contains (element
))
294 perform (new FrontOrBackElementAction (element
, -1), "Move elements to front");
296 moveElementZOrder (elements
.indexOf (element
), -1);
300 void PaintRoutine::elementToBack (PaintElement
* element
, const bool undoable
)
302 if (element
!= 0 && elements
.contains (element
))
305 perform (new FrontOrBackElementAction (element
, 0), "Move elements to back");
307 moveElementZOrder (elements
.indexOf (element
), 0);
311 //==============================================================================
312 const char* const PaintRoutine::clipboardXmlTag
= "PAINTELEMENTS";
314 void PaintRoutine::copySelectedToClipboard()
316 if (selectedElements
.getNumSelected() == 0)
319 XmlElement
clip (clipboardXmlTag
);
321 for (int i
= 0; i
< elements
.size(); ++i
)
323 PaintElement
* const pe
= elements
.getUnchecked(i
);
325 if (selectedElements
.isSelected (pe
))
327 XmlElement
* const e
= pe
->createXml();
328 clip
.addChildElement (e
);
332 SystemClipboard::copyTextToClipboard (clip
.createDocument (String::empty
, false, false));
335 void PaintRoutine::paste()
337 XmlDocument
clip (SystemClipboard::getTextFromClipboard());
338 XmlElement
* const doc
= clip
.getDocumentElement();
340 if (doc
!= 0 && doc
->hasTagName (clipboardXmlTag
))
342 selectedElements
.deselectAll();
343 selectedPoints
.deselectAll();
345 forEachXmlChildElement (*doc
, e
)
347 PaintElement
* newElement
= addElementFromXml (*e
, -1, true);
350 selectedElements
.addToSelection (newElement
);
357 void PaintRoutine::deleteSelected()
359 const SelectedItemSet
<PaintElement
*> temp1 (selectedElements
);
360 const SelectedItemSet
<PathPoint
*> temp2 (selectedPoints
);
362 if (temp2
.getNumSelected() > 0)
364 selectedPoints
.deselectAll();
365 selectedPoints
.changed (true); // synchronous message to get rid of any property components
367 // if any points are selected, just delete them, and not the element, which may
368 // also be selected..
369 for (int i
= temp2
.getNumSelected(); --i
>= 0;)
370 temp2
.getSelectedItem (i
)->deleteFromPath();
374 else if (temp1
.getNumSelected() > 0)
376 selectedElements
.deselectAll();
377 selectedElements
.changed (true);
379 for (int i
= temp1
.getNumSelected(); --i
>= 0;)
380 removeElement (temp1
.getSelectedItem (i
), true);
386 void PaintRoutine::selectAll()
388 if (selectedPoints
.getNumSelected() > 0)
390 PaintElementPath
* path
= selectedPoints
.getSelectedItem (0)->owner
;
394 for (int i
= 0; i
< path
->getNumPoints(); ++i
)
395 selectedPoints
.addToSelection (path
->getPoint (i
));
400 for (int i
= 0; i
< elements
.size(); ++i
)
401 selectedElements
.addToSelection (elements
.getUnchecked (i
));
405 void PaintRoutine::selectedToFront()
407 const SelectedItemSet
<PaintElement
*> temp (selectedElements
);
409 for (int i
= temp
.getNumSelected(); --i
>= 0;)
410 elementToFront (temp
.getSelectedItem(i
), true);
413 void PaintRoutine::selectedToBack()
415 const SelectedItemSet
<PaintElement
*> temp (selectedElements
);
417 for (int i
= 0; i
< temp
.getNumSelected(); ++i
)
418 elementToBack (temp
.getSelectedItem(i
), true);
421 void PaintRoutine::groupSelected()
423 PaintElementGroup::groupSelected (this);
426 void PaintRoutine::ungroupSelected()
428 const SelectedItemSet
<PaintElement
*> temp (selectedElements
);
430 for (int i
= 0; i
< temp
.getNumSelected(); ++i
)
432 PaintElementGroup
* const pg
= dynamic_cast <PaintElementGroup
*> (temp
.getSelectedItem (i
));
439 void PaintRoutine::bringLostItemsBackOnScreen (const Rectangle
<int>& parentArea
)
441 for (int i
= 0; i
< elements
.size(); ++i
)
443 PaintElement
* const c
= elements
[i
];
445 Rectangle
<int> r (c
->getCurrentBounds (parentArea
));
447 if (! r
.intersects (parentArea
))
449 r
.setPosition (parentArea
.getCentreX(), parentArea
.getCentreY());
450 c
->setCurrentBounds (r
, parentArea
, true);
455 void PaintRoutine::startDragging (const Rectangle
<int>& parentArea
)
457 for (int i
= 0; i
< elements
.size(); ++i
)
459 PaintElement
* const c
= elements
[i
];
461 Rectangle
<int> r (c
->getCurrentBounds (parentArea
));
463 c
->getProperties().set ("xDragStart", r
.getX());
464 c
->getProperties().set ("yDragStart", r
.getY());
467 getDocument()->getUndoManager().beginNewTransaction();
470 void PaintRoutine::dragSelectedComps (int dx
, int dy
, const Rectangle
<int>& parentArea
)
472 getDocument()->getUndoManager().undoCurrentTransactionOnly();
474 if (document
!= 0 && selectedElements
.getNumSelected() > 1)
476 dx
= document
->snapPosition (dx
);
477 dy
= document
->snapPosition (dy
);
480 for (int i
= 0; i
< selectedElements
.getNumSelected(); ++i
)
482 PaintElement
* const c
= selectedElements
.getSelectedItem (i
);
484 const int startX
= c
->getProperties() ["xDragStart"];
485 const int startY
= c
->getProperties() ["yDragStart"];
487 Rectangle
<int> r (c
->getCurrentBounds (parentArea
));
489 if (document
!= 0 && selectedElements
.getNumSelected() == 1)
491 r
.setPosition (document
->snapPosition (startX
+ dx
),
492 document
->snapPosition (startY
+ dy
));
496 r
.setPosition (startX
+ dx
,
500 c
->setCurrentBounds (r
, parentArea
, true);
506 void PaintRoutine::endDragging()
508 getDocument()->getUndoManager().beginNewTransaction();
511 //==============================================================================
512 void PaintRoutine::fillWithBackground (Graphics
& g
, const bool drawOpaqueBackground
)
514 if ((! backgroundColour
.isOpaque()) && drawOpaqueBackground
)
516 g
.fillCheckerBoard (Rectangle
<int> (0, 0, g
.getClipBounds().getRight(), g
.getClipBounds().getBottom()),
518 Colour (0xffdddddd).overlaidWith (backgroundColour
),
519 Colour (0xffffffff).overlaidWith (backgroundColour
));
523 g
.fillAll (backgroundColour
);
527 void PaintRoutine::drawElements (Graphics
& g
, const Rectangle
<int>& relativeTo
)
530 temp
.setBounds (relativeTo
);
532 for (int i
= 0; i
< elements
.size(); ++i
)
533 elements
.getUnchecked (i
)->draw (g
, getDocument()->getComponentLayout(), relativeTo
);
536 //==============================================================================
537 void PaintRoutine::dropImageAt (const File
& f
, int x
, int y
)
539 Drawable
* d
= Drawable::createFromImageFile (f
);
543 Rectangle
<float> bounds (d
->getDrawableBounds());
546 PaintElement
* newElement
547 = addNewElement (ObjectTypes::createNewImageElement (this), -1, true);
549 PaintElementImage
* pei
= dynamic_cast <PaintElementImage
*> (newElement
);
553 String
resourceName (getDocument()->getResources().findUniqueName (f
.getFileName()));
555 const BinaryResources::BinaryResource
* existingResource
= getDocument()->getResources().getResourceForFile (f
);
557 if (existingResource
!= 0)
559 resourceName
= existingResource
->name
;
564 f
.loadFileAsData (data
);
566 getDocument()->getResources().add (resourceName
, f
.getFullPathName(), data
);
569 pei
->setResource (resourceName
, true);
571 const int imageW
= (int) (bounds
.getRight() + 0.999f
);
572 const int imageH
= (int) (bounds
.getBottom() + 0.999f
);
574 RelativePositionedRectangle pr
;
575 pr
.rect
.setX (x
- imageW
/ 2);
576 pr
.rect
.setY (y
- imageH
/ 2);
577 pr
.rect
.setWidth (imageW
);
578 pr
.rect
.setHeight (imageH
);
580 pei
->setPosition (pr
, true);
582 getSelectedElements().selectOnly (pei
);
587 //==============================================================================
588 const char* PaintRoutine::xmlTagName
= "BACKGROUND";
590 XmlElement
* PaintRoutine::createXml() const
592 XmlElement
* const xml
= new XmlElement (xmlTagName
);
594 xml
->setAttribute ("backgroundColour", colourToHex (backgroundColour
));
596 for (int i
= 0; i
< elements
.size(); ++i
)
597 xml
->addChildElement (elements
.getUnchecked (i
)->createXml());
602 bool PaintRoutine::loadFromXml (const XmlElement
& xml
)
604 if (xml
.hasTagName (xmlTagName
))
606 backgroundColour
= Colour (xml
.getStringAttribute ("backgroundColour", colourToHex (Colours::white
)).getHexValue32());
610 forEachXmlChildElement (xml
, e
)
612 PaintElement
* const newElement
= ObjectTypes::createElementForXml (e
, this);
615 elements
.add (newElement
);
624 void PaintRoutine::fillInGeneratedCode (GeneratedCode
& code
, String
& paintMethodCode
) const
626 if (! backgroundColour
.isTransparent())
627 paintMethodCode
<< "g.fillAll (" << colourToCode (backgroundColour
) << ");\n\n";
629 for (int i
= 0; i
< elements
.size(); ++i
)
630 elements
[i
]->fillInGeneratedCode (code
, paintMethodCode
);